#include "kernel/proc.h"
#include "kernel/spinlock.h"
#include "kernel/semaphore.h"

DECLARE_PRIVATE_SPINLOCK(sem_spinlock);

void down(semaphore_t* sem)
{
	thread_t *thd = current;
	DECLARE_WAIT_QUEUE(wait, thd);
	if (!thd) return;

	set_current_state(THD_STAT_UNINTERRUPTABLE, true);
	add_wait_queue(&sem->wait, &wait);

	spin_lock(&sem_spinlock);
	sem->sleepers++;

	while (1)
	{
		int sleepers = sem->sleepers;
		if (!atomic_add_negative(sleepers - 1, &sem->count))
		{
			sem->sleepers = 0;
			break;
		}
		sem->sleepers = 1;
		spin_unlock(&sem_spinlock);

		schedule();
		set_current_state(THD_STAT_UNINTERRUPTABLE, true);
		spin_lock(&sem_spinlock);
	}
	spin_unlock(&sem_spinlock);
	remove_wait_queue(&sem->wait, &wait);
	set_current_state(THD_STAT_RUNNING, true);
	wake_up(&sem->wait, WQ_FLAG_WAKEUP_UNINTERRUPTABLE);
}

void up(semaphore_t* sem)
{
	wake_up(&sem->wait, WQ_FLAG_WAKEUP_UNINTERRUPTABLE);
}
